package jeconbond.experiment.natural.prodpossibfrontier.generate;

import jeconbond.automata.INode;
import jeconbond.automata.IOutcome;
import jeconbond.automata.Node;
import jeconbond.automata.Outcome;
import jeconbond.experiment.generate.IAutomataGenerator;
import jeconbond.economic.resources.ResourceBasket;
import jeconbond.experiment.natural.prodpossibfrontier.process.IConneryExperimentContext;
import jeconbond.experiment.natural.prodpossibfrontier.ConneryExperimentSettings;
import jeconbond.Q.computation.IProgressFunc;
import jeconbond.Q.computation.ProdPossProgressFunc;
import jeconbond.experiment.process.IJEBExperimentContext;
import laboratoryQ.BaseUtils;

public class AdaptiveProdPPFGraphGenerator implements IAutomataGenerator {
	@Override
	public INode[] generate(IJEBExperimentContext iexperimentContext) {
		IConneryExperimentContext experimentContext = 
			(IConneryExperimentContext) iexperimentContext;
		ConneryExperimentSettings settings = experimentContext.getExperimentSettings();
		int n = settings.agentsCount;
		INode[] roots = new INode[n];
		double[] relationBuffer = new double[settings.branchesCount];
		for (int i = 0; i < n; i++) {
			roots[i] = generateOne(
					i, 
					n, 
					relationBuffer,
					experimentContext
					);
		}
		return roots;
	}

	private static INode generateOne(
			int curr, 
			int count,
			double[] relationsBuffer,
			IConneryExperimentContext experimentContext
	) {
		ConneryExperimentSettings settings = experimentContext.getExperimentSettings();
		double relationBoard = settings.relationBoard;
		fillBrancheRelations(
				pos2relation(
						curr, 
						count, 
						relationBoard
				), 
				settings, 
				relationsBuffer
		);
		String namePref = "AdaptT&T:" + curr + "from" + count;
		INode root = new Node(namePref + "root");
		int n = relationsBuffer.length;
		double[] tmp2 = new double[2];
		for (int i = 0; i < n; i++) {
			generateBranch(
					root, 
					experimentContext,
					relationsBuffer[i], 
					namePref + "Br" + i, 
					tmp2
			);
		}
		return root;
	}

	static { System.out.println("\nBLOCKING SMALL generate with Utils"); }

	private static void generateBranch(
			INode root,
			IConneryExperimentContext experimentContext,
			double curSpec,
			String namePref,
			double[] tmp2
	) {
		INode prev = root;
		double ironPerStep = 1.0;
		ConneryExperimentSettings settings = experimentContext.getExperimentSettings();
		for (int i = 0; i < settings.chainLength; i++) { // chainLength
//		for (int i = 0; i < settings.branchLength; i++) { // chainLength
			INode n = new Node(namePref + "St" + i);
			IOutcome out = new Outcome(
					n, 
					new ResourceBasket(
							settings.ironResource,
							ironPerStep
					)
			);
			prev.getOutcomes().add(out);
			prev = n;
			if (i == settings.chainLength - 1) {
//			if (i == settings.branchLength - 1) {
				IOutcome prodOut = new Outcome(root);
				n.getOutcomes().add(prodOut);
				addProduced(prodOut, experimentContext, curSpec, tmp2);
			}
		}
	}
	
	static { System.out.println("\nOLD_EXPERIMENT very bad IProgressFunc progressFunc = new ProdPossProgressFunc( !"); }
	
	private static void addProduced(
			IOutcome out,
			IConneryExperimentContext experimentContext,
			double goalRel,
			double[] tmp
			) {
		ConneryExperimentSettings settings = experimentContext.getExperimentSettings();
		IProgressFunc progressFunc = new ProdPossProgressFunc(
				settings.tankMul,
				settings.traktorMul
		); 
		progressFunc.compute(goalRel, tmp);
		double tankPart = tmp[0];
		double traktorPart = tmp[1];
		tankPart = reduce(tankPart, experimentContext);
		traktorPart = reduce(traktorPart, experimentContext);
		double lengthMul = (settings.chainLength + 1) / 2.0; // ??? 8-)
//		double lengthMul = (settings.branchLength + 1) / 2.0; // ??? 8-)
		out.setProducedResources(
				new ResourceBasket(
						settings.tankResource,
						tankPart * lengthMul
				),
				new ResourceBasket(
						settings.traktorResource,
						traktorPart * lengthMul
				)
		);
	}
	
	private static double reduce(
			double val,
			IConneryExperimentContext experimentContext
	) {
		ConneryExperimentSettings settings = experimentContext.getExperimentSettings();
		val += BaseUtils.randomFromDist(experimentContext.getRandom(), -val, val) * settings.fluctuationPart;
		return val;
	}
	
	private static double pos2relation(double curr, double count, double relationBoard) {
		curr += relationBoard;
		count += relationBoard;
		return curr / count;
	}

	public static void fillBrancheRelations(
			double centralRelation,
			ConneryExperimentSettings settings, double[] relationsBuffer
			) {
		double centralEtalLn = Math.log(centralRelation);
		double relDistBeg = centralEtalLn * (1 - settings.relationDistPercent / 2);
		double relDistEnd = centralEtalLn * (1 + settings.relationDistPercent / 2);
		if (relDistBeg > relDistEnd) {
			double t = relDistBeg; relDistBeg = relDistEnd; relDistEnd = t;
		}
		double step = (relDistEnd - relDistBeg) / (double)settings.branchesCount;
		int n = relationsBuffer.length;
		for (int i = 0; i < n; i++) {
			double curRel = Math.exp(relDistBeg + i * step);
			relationsBuffer[i] = curRel;
		}
	}
}
